package simpledb.storage;

import simpledb.util.IteratorWrapper;

import java.io.Serializable;
import java.util.Arrays;
import java.util.Iterator;

/**
 * 数据库中的行被称为记录 record 或元组 tuple
 *         列被称为字段 Field或属性 attribute
 * tuples 是一组 Field 对象的集合，Field 是不同数据类型(Integer, String 等)实现的接口
 * Tuple 对象由底层访问方法如 heap files, B trees 创建
 * Tuple 也有类型 type (或被称为组织结构 schema)，称为 _tuple descriptor_，是 TypeDesc 对象，这个对象包括了 Type 对象的集合
 * 这个类的主要功能包括：
 * 1、根据给定的元组描述（TupleDesc）创建元组；
 * 2、设置和获取字段内容；
 * 3、记录该元组在磁盘上的位置（通过 RecordId）；
 * 4、提供迭代器遍历所有字段；
 * 5、支持重置元组结构（但不改变字段数据）；
 * 6、可以被序列化，便于网络传输或持久化。
 */
public class Tuple implements Serializable {

    private static final long serialVersionUID = 1L;

    // 元组的描述信息（即 schema）
    private TupleDesc         tupleDesc;

    // 存储元组的所有字段值--- 各个列
    private Field[]           fields;

    // 该元组在磁盘上的物理位置标识符（可能为 null）
    private RecordId          recordId;

    /**
     * 构造函数：根据指定的元组描述（schema）创建一个新的元组。
     *
     * @param tupleDesc 元组的描述对象（必须是一个有效的 TupleDesc 实例，并且至少有一个字段）
     */
    public Tuple(TupleDesc tupleDesc) {
        this.tupleDesc = tupleDesc; // 设置元组的描述信息
        this.fields = new Field[this.tupleDesc.numFields()]; // 初始化字段数组
    }

    /**
     * 获取当前元组的描述对象（schema）。
     *
     * @return 当前元组的 TupleDesc 对象
     */
    public TupleDesc getTupleDesc() {
        return this.tupleDesc;
    }

    /**
     * 获取当前元组在磁盘上的物理位置标识符。
     *
     * @return 如果设置了 RecordId，则返回该对象；否则返回 null
     */
    public RecordId getRecordId() {
        return this.recordId;
    }

    /**
     * 设置当前元组的 RecordId（即在磁盘上的存储位置）。
     *
     * @param rid 新的 RecordId
     */
    public void setRecordId(RecordId rid) {
        this.recordId = rid;
    }

    /**
     * 修改当前元组中第 i 个字段的值。
     *
     * @param i 字段索引（从 0 开始）
     * @param f 要设置的新字段值
     */
    public void setField(int i, Field f) {
        if (i >= this.tupleDesc.numFields()) {
            return; // 防止越界
        }
        this.fields[i] = f;
    }

    /**
     * 获取当前元组中第 i 个字段的值。
     *
     * @param i 字段索引（从 0 开始）
     * @return 返回对应的 Field 对象，如果未设置则返回 null
     */
    public Field getField(int i) {
        if (i >= this.tupleDesc.numFields()) {
            return null; // 防止越界
        }
        return this.fields[i];
    }

    /**
     * 将当前元组转换为字符串形式，用于调试输出。
     * 系统测试要求格式为：
     * column1\tcolumn2\tcolumn3\t...\tcolumnN
     * 其中 \t 是任意空白字符（除换行外）
     */
    @Override
    public String toString() {
        return "Tuple{" + "tupleDesc=" + tupleDesc + ", fields=" + Arrays.toString(fields) + ", recordId=" + recordId
               + '}';
    }

    /**
     * 返回一个迭代器，用于遍历该元组的所有字段。
     *
     * @return Iterator<Field> 迭代器对象
     */
    public Iterator<Field> fields() {
        return new IteratorWrapper<>(this.fields);
    }

    /**
     * 重置当前元组的描述信息（只影响 tupleDesc，不影响字段内容）。
     *
     * @param td 新的元组描述对象
     */
    public void resetTupleDesc(TupleDesc td) {
        this.tupleDesc = td;
    }
}
